package com.media.entrance.websocket;


import com.media.entrance.service.MatchService;
import com.media.entrance.service.RedisService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.Date;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

@Component
@Slf4j
@ServerEndpoint("/client/websocket/{deviceId}")
public class WebSocketServer {
    private static MatchService matchService;
    private static AsyncTask asyncTask;
    @Autowired
    public void setMatchService(MatchService matchService){
        WebSocketServer.matchService = matchService;
    }
    @Autowired
    public void setAsyncTask(AsyncTask asyncTask){
        WebSocketServer.asyncTask = asyncTask;
    }

    private static String nacosRegisterIp;
    @Value("${nacos.discovery.register.ip}")
    public void setNacosRegisterIp(String ip) {
        WebSocketServer.nacosRegisterIp = ip;
    }


    /** 当前在线客户端数量（线程安全的） */
    private static AtomicInteger onlineClientNumber = new AtomicInteger(0);

    public static Map<String, Session> getOnlineClientMap() {
        return onlineClientMap;
    }

    /** 当前在线客户端集合（线程安全的）：以键值对方式存储，key是连接的编号，value是连接的对象 */
    private static Map<String ,Session> onlineClientMap = new ConcurrentHashMap<>();

    /**
     * 客户端与服务端连接成功
     * @param session 会话信息
     * @param deviceId 设备信息
     */
    @OnOpen
    public void onOpen(Session session,@PathParam("deviceId") String deviceId){
        /*
            do something for onOpen
            与当前客户端连接成功时
         */
        if (deviceId.equals("")) {
            log.error("不允许websocket的设备号为空");
            try {
                session.close();
            } catch (Exception e){
                log.error(e.toString());
            }
            return;
        }
        onlineClientNumber.incrementAndGet();//在线数+1
        onlineClientMap.put(deviceId, session);//添加当前连接的session
        log.info("时间[{}]：与用户[{}]的连接成功，当前连接编号[{}]，当前连接总数[{}]",
                new Date(),
                deviceId,
                session.getId(),
                onlineClientNumber);
    }

    /**
     * 客户端与服务端连接关闭
     * @param session 会话信息
     * @param deviceId 设备信息
     */
    @OnClose
    public void onClose(Session session,@PathParam("deviceId") String deviceId){
        /*
            do something for onClose
            与当前客户端连接关闭时
         */
        onlineClientNumber.decrementAndGet();//在线数-1
        if(session != null) {
            onlineClientMap.remove(session.getId());//移除当前连接的session
            log.info("时间[{}]：与用户[{}]的连接关闭，当前连接编号[{}]，当前连接总数[{}]",
                    new Date(),
                    deviceId,
                    session.getId(),
                    onlineClientNumber);
            // websocket 断开时 触发清理，只有再状态不为busy时 清pgdb或redis
            Set<String> needInformDeviceIds = matchService.cancelMatch(deviceId);
            // 清理时发现该设备再排队中，才会通知其他排队的设备
            if (needInformDeviceIds != null) {
                for (String devId : needInformDeviceIds) {
                    asyncTask.informDeviceNewPostionInQueue(devId);
                }
            }
        } else {
            log.error("sesson is null");
        }
    }

    /**
     * 客户端与服务端连接异常
     * @param error 报错信息
     * @param session 会话信息
     * @param deviceId 设备信息
     */
    @OnError
    public void onError(Throwable error,Session session,@PathParam("deviceId") String deviceId) {
        /*
            do something for onError
            与当前客户端连接异常时
         */
        error.printStackTrace();
    }

    /**
     * 客户端向服务端发送消息
     * @param message 发送的消息
     * @param deviceId 设备信息
     */
    @OnMessage
    public void onMsg(Session session,String message,@PathParam("deviceId") String deviceId) {
        /*
            do something for onMessage
            收到来自当前客户端的消息时
         */
        log.info("时间[{}]：来自连接编号为[{}]的消息：[{}]",
                new Date(),
                session.getId(),
                message);
        // 匹配逻辑
        if (message.equals("requestMatchAgent")) {
            // 做一次匹配
            asyncTask.doOneMatch(deviceId);
        } else{
            sendOneMessage(deviceId, message);
        }
    }

    //向所有客户端发送消息（广播）
    private void sendAllMessage(String message){
        Set<String> deviceIds = onlineClientMap.keySet(); //获得Map的Key的集合
        for (String deviceId : deviceIds) { //迭代Key集合
            Session session = onlineClientMap.get(deviceId); //根据Key得到value
            if (session.isOpen()) {
                session.getAsyncRemote().sendText(message); //发送消息给客户端
            }
        }
    }

    //只向当前客户端发送消息
    private void sendOneMessage(String deviceId, String message){
        Session session = onlineClientMap.get(deviceId);
        if (session.isOpen()) {
            session.getAsyncRemote().sendText(message); //发送消息给客户端
        }
    }

}
